# -*- coding: utf-8 -*-

import ast

from ir.ir_type import *
from ir.ir_ports import *
from ir.ir_ds import *
from ir.ir_node import *
from ir.ir_statement import *
from ir.ir_helper import Helper


def convert_str_to_type(pytype: str):
    # Check python type.
    if pytype == 'int':
        return ITypeInt
    if pytype == 'float':
        return ITypeFloat
    if pytype == 'complex':
        return ITypeComplex
    if pytype == 'str':
        return ITypeStr
    if pytype == 'bool':
        return ITypeBool
    return None


LIST_PREFIX = 'List.'


def convert_ast_to_type(ast_type, helper: Helper):
    if ast_type is None:
        return None
    if isinstance(ast_type, ast.Name):
        return convert_ast_to_type(ast_type.id, helper)
    if isinstance(ast_type, ast.Str):
        return convert_ast_to_type(ast_type.s, helper)
    if isinstance(ast_type, str):
        # Try to convert from basic type.
        basic_test = convert_str_to_type(ast_type)
        if basic_test is not None:
            return basic_test
        # Check for special cases.
        if ast_type == 'T':
            return IGeneric('T')
        if ast_type == 'type' or ast_type == 'any':
            return IType
        if ast_type == 'PortIn':
            return ITypePortIn
        if ast_type == 'PortInDirect':
            return ITypePortInDirect
        if ast_type == 'PortOut':
            return ITypePortOut
        if ast_type == 'LanePort':
            return ITypeLanePort
        if ast_type == 'Node':
            return ITypeNode
        if ast_type == 'LanePortSender':
            return ITypeLanePortSender
        if ast_type == 'LanePortReceiver':
            return ITypeLanePortReceiver
        if ast_type == 'ChannelPortSender':
            return ITypeChannelPortSender
        if ast_type == 'ChannelPortReceiver':
            return ITypeChannelPortReceiver
        if ast_type == 'list':
            return ITypeList
        if ast_type == 'dict' or ast_type == 'ObjectDict':
            return ITypeStruct
        if helper is not None:
            if ast_type in helper.types:
                return helper.types[ast_type]
        if ast_type.startswith(LIST_PREFIX):
            # It is a list type definition, extract the managed type.
            managed_type = convert_ast_to_type(ast_type[len(LIST_PREFIX):], helper)
            # Give back the list of managed type.
            return ITypeList(managed_type)
        raise Exception('Failed to convert type {}'.format(ast_type))
    if isinstance(ast_type, ast.NameConstant):
        return convert_ast_to_type(ast_type.value, helper)
    if isinstance(ast_type, IOpCall):
        # Get the return type.
        return ast_type.funcdef.ret_type
    if isinstance(ast_type, IExpValue) or isinstance(ast_type, IVarReg) or \
            isinstance(ast_type, IValueListComp) or isinstance(ast_type, IVarParam) or \
            isinstance(ast_type, IExpUnary) or isinstance(ast_type, IExpBinary) or \
            isinstance(ast_type, IExpMember):
        # Well, it hold the type for itself.
        return ast_type.type
    if isinstance(ast_type, IOpLen):
        return ITypeInt
    if isinstance(ast_type, IOpTypeConvert) or isinstance(ast_type, IVar) or \
            isinstance(ast_type, IExpSubscript) or isinstance(ast_type, IOpConstruct):
        return ast_type.type
    raise Exception('Unimplemented type convert from ast {}'.format(type(ast_type)))


def get_ir_type(ir_data):
    # If the variable is just a simple value, then that's easy.
    if isinstance(ir_data, IVar):
        return ir_data.type
    raise Exception('Unknown IR data type {}'.format(type(ir_data)))